// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.particles {
	import actionml.Global;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
		
	public class ParticleMethods {
		public static function initialize() {
			Global.initializeExtension(ParticleMethods, DisplayObject);
		}
		public static var instanceMethods:Object = {
			makeParticleGenerator: function(options:Object = null) {
				if (this.isAlready("ParticleGenerator")) return;
				this.options = options || {};
				this.particleClasses = [];
				this.particles = [];
				for each (var className:String in options["particles"].split(/\s*,\s*/)) {
					this.particleClasses.push(Class["forName"](className));
				}
			},
			drawCurve: function(points:Array = null) {
			    points ||= [];
				//var points:Array = [{x:-50, y:-100}, {x:50, y:50}, {x:30, y:100}, {x:45, y: -90}];
				if (points.length == 0) {
				    for (var i:int = 0; i < 6; i++) {
				        points.push({
				            x: Math.random() * this.stage.stageWidth - this.x, 
				            y: Math.random() * this.stage.stageHeight - this.y}
				        );
				    }
				}
				var g = this.graphics;
				g.lineStyle(1);
				/* 0, 100, 30
					0, -50, 20 */
				//g.moveTo(0, 0); 				
				//g.curveTo(185, -110, 30, 20);
				//g.curveTo(10, 15, 0, 0);
				//g.beginFill(0xFF00FF);
				//g.drawCircle(0, 0, 3);
				//g.drawCircle(100, -50, 3);
				//g.drawCircle(30, 20, 3);
				//g.endFill();
				g.moveTo(
					(points[0].x + points[points.length-1].x) / 2, 
					(points[0].y + points[points.length-1].y) / 2
				);
				g.curveTo(
					points[0].x * 2 - (points[0].x * 2 + points[points.length-1].x + points[1].x) / 4, 
					points[0].y * 2 - (points[0].y * 2 + points[points.length-1].y + points[1].y) / 4,
					(points[0].x + points[1].x) / 2, 
					(points[0].y + points[1].y) / 2
				);
				for (i = 1; i < points.length - 1; i++) {
					g.curveTo(
						points[i].x * 2 - (points[i].x * 2 + points[i-1].x + points[i+1].x) / 4, 
						points[i].y * 2 - (points[i].y * 2 + points[i-1].y + points[i+1].y) / 4, 
						(points[i].x + points[i+1].x) / 2, 
						(points[i].y + points[i+1].y) / 2
						/*points[i].x, points[i].y,
						(points[i].x + points[i+1].x) / 2,
						(points[i].y + points[i+1].y) / 2*/
					);
				}
				/*g.curveTo(
					points[i].x, points[i].y,
					(points[0].x + points[points.length-1].x) / 2,
                    (points[0].y + points[points.length-1].y) / 2
				);*/   				
				g.curveTo(
					points[i].x * 2 - (points[i].x * 2 + points[i-1].x + points[0].x) / 4, 
					points[i].y * 2 - (points[i].y * 2 + points[i-1].y + points[0].y) / 4,
					(points[i].x + points[0].x) / 2, 
					(points[i].y + points[0].y) / 2
				);
				g.beginFill(0xFF00FF);
				for (i = 0; i < points.length; i++) {
					g.drawCircle(points[i].x, points[i].y, 3);
				}
				g.drawCircle(
                    (points[0].x + points[points.length-1].x) / 2 + 5,
                    (points[0].y + points[points.length-1].y) / 2 + 5,
                    5
				);
				g.endFill();
			},
			turnOnParticles: function() {
				var o:DisplayObject = this;	
				var currentFrame:int = 0;		
				var nextParticleFrame:int = 1;
				o.addEventListener(Event.ENTER_FRAME, function() {
					currentFrame++;
					var p:DisplayObject;
					for each (p in o.particles) {
						var ax:Number = 0;
						var ay:Number = 0;
						for each (var f:Object in p.forces) {
							ax += f.magnitude * Math.cos(f.direction);
							ay += f.magnitude * Math.sin(f.direction);
						}
						var vx:Number = p.speed * Math.cos(p.direction) + ax;
						var vy:Number = p.speed * Math.sin(p.direction) + ay;
						p.direction = Math.atan2(vy, vx);
						p.speed = Math.sqrt(vx * vx + vy * vy);
						if (p.speed > p.friction) {
							p.speed -= p.friction;
						} else {
							p.speed = 0;
						}
						p.x += vx;
						p.y += vy;
						if (p.y + p.height > o.stage.stageHeight) {
							p.y = o.stage.stageHeight - p.height;
							p.speed -= p.absorption;
							if (p.direction > Math.PI / 2) { // should really test normalized angle
								p.direction = Math.PI * 2 - p.direction;
							} else {
								p.direction *= -1;
							}
						}
					}
					if (o.particles.length < o.options.count && currentFrame == nextParticleFrame) {
						p = new (o.particleClasses.random())();
						p.x = o.x;
						p.y = o.y;
						p.speed = o.options.speed;
						p.friction = o.options.friction;
						p.absorption = o.options.absorption;
						p.direction = -Math.PI / 12 - Math.random() * (Math.PI - Math.PI / 6);	
						p.forces = [
							{direction: Math.PI / 2, magnitude: o.options.gravity},
							{direction: p.direction, magnitude: o.options.accelleration}
						];
						o.particles.push(p);
						o.parent.addChild(p);
						nextParticleFrame += o.options.interval;
					}
				});	
			}
		};
	}
}